package com.example.demo.config.jwt;

import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.example.demo.utils.RedisUtil;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.mybatis.logging.Logger;
import org.mybatis.logging.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * jwt 的过滤器, 是Shiro 的过滤器，然后重写了，需要用自己定义的 jwt 方式来验证
 * 类的话，有很多种
 */
public class JwtFilter extends BasicHttpAuthenticationFilter {


    private Logger LOGGER = LoggerFactory.getLogger(this.getClass());


//    @Override
//    protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) {
//        String jwtToken = getRequestToken((HttpServletRequest) servletRequest);
//        return new JwtToken(jwtToken);
//    }

    /**
     * 检测Header里Authorization字段，如果没有字段的话，就直接驳回，为无效登录
     *
     * @param request
     * @param response
     * @return
     */
    @Override
    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
        System.out.println("1. ====验证是否带有头标识====");
        HttpServletRequest req = (HttpServletRequest) request;
        // SecurityConsts.REQUEST_AUTH_HEADER 就是 "Authorization"
        String authorization = req.getHeader(JwtProperties.REQUEST_AUTH_HEADER);
        return authorization != null;
    }

    /**
     * 登录验证
     *
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {

        System.out.println("2. ====登录验证====");
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        // 获取 头标识
        String authorization = httpServletRequest.getHeader(JwtProperties.REQUEST_AUTH_HEADER);
        JwtToken token = new JwtToken(authorization);
        // 提交给realm进行登入，如果错误他会抛出异常并被捕获
        getSubject(request, response).login(token);
        return true;
    }

    /**
     * 刷新Token，进行判断RefreshToken是否过期，未过期就返回新的Token且继续正常访问
     */
    private boolean refreshToken(ServletRequest request, ServletResponse response) {

        // 获取AccessToken(Shiro中getAuthzHeader方法已经实现)
        String token = this.getAuthzHeader(request);

        // 获取当前Token的帐号信息
        String account = JwtUtil.getClaim(token, JwtProperties.ACCOUNT);

        // 获取放置在 redis 中 refreshtoken 信息
        String refreshTokenCacheKey = JwtProperties.PREFIX_REDIS_REFRESH_TOKEN + account;
        RedisTemplate redisTemplate = RedisUtil.get();

        // 判断Redis中RefreshToken是否存在
        if (redisTemplate.hasKey(refreshTokenCacheKey)) {
            System.out.println(System.currentTimeMillis());
            System.out.println(redisTemplate.opsForValue().get(refreshTokenCacheKey));
            Long i = Long.parseLong((String)redisTemplate.opsForValue().get(refreshTokenCacheKey)) ;
            // refreshtoken 已经过期

            if (i <= System.currentTimeMillis()) {
                return false;
            }
            // 获取RefreshToken时间戳,及AccessToken中的时间戳
            // 相比如果一致，进行AccessToken刷新
            String currentTimeMillisRedis = (String) redisTemplate.opsForValue().get(refreshTokenCacheKey);

            String tokenMillis = JwtUtil.getClaim(token, JwtProperties.REFRESH_TIME_MILLIS);

            // 如果 redis 中的refresh 和
            if (tokenMillis.equals(currentTimeMillisRedis)) {
                System.out.println("重新开始属性了");
                long currentTimeMillis = System.currentTimeMillis();

                // 刷新Token，为当前最新时间戳
                token = JwtUtil.sign(account, currentTimeMillis);
                // 使用Token 再次提交给ShiroRealm进行认证，如果没有抛出异常则登入成功，返回true

                JwtToken jwtToken = new JwtToken(token);
                this.getSubject(request, response).login(jwtToken);

                // 设置响应的Header头新Token
                HttpServletResponse httpServletResponse = (HttpServletResponse) response;
                httpServletResponse.setHeader(JwtProperties.REQUEST_AUTH_HEADER, token);
                httpServletResponse.setHeader("Access-Control-Expose-Headers", JwtProperties.REQUEST_AUTH_HEADER);
                return true;
            }
        }
        return false;
    }

    /**
     * 这里就是验证 token是否有效的地方
     *
     * @param request
     * @param response
     * @param mappedValue
     * @return
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {

        System.out.println("进来开始验证了");
        // 判断是否有头标识，带有头token
        if (isLoginAttempt(request, response)) {
            try {
                // 执行登录
                this.executeLogin(request, response);

                System.out.println("3.====isAccessAllowed====");
            } catch (Exception e) {
                System.out.println("isAccessAllowed 中token 验证失败 看一下 是否有refreshtoken");
                String msg = e.getMessage();
                Throwable throwable = e.getCause();
                if (throwable != null && throwable instanceof SignatureVerificationException) {
                    msg = "Token或者密钥不正确(" + throwable.getMessage() + ")";
                } else if (throwable != null && throwable instanceof TokenExpiredException) {
                    System.out.println("验证 refreshtoken是否过期");
                    // token已过期
                    if (this.refreshToken(request, response)) {
                        return true;
                    } else {
                        msg = "refreshToken已过期(" + throwable.getMessage() + ")";
                    }
                } else {
                    if (throwable != null) {
                        msg = throwable.getMessage();
                    }
                }
                this.response401(request, response, msg);
                return false;
            }
        } else {
            // 没有头标识，直接返回不能登录
            return false;
        }
        return true;
    }

    /**
     * 401非法请求
     *
     * @param req
     * @param resp
     */
    private void response401(ServletRequest req, ServletResponse resp, String msg) {

        HttpServletResponse httpServletResponse = (HttpServletResponse) resp;
        httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("application/json; charset=utf-8");
        PrintWriter out = null;
        try {
            // 设置返回信息
            out = httpServletResponse.getWriter();
//            ResultData result = new ResultData();
//            result.setCode(Constants.PASSWORD_CHECK_INVALID);
//            result.setMessage(msg);
            out.append("失败");

        } catch (IOException e) {
//
        } finally {
            if (out != null) {
                out.close();
            }
        }
    }
}